/**
 * 
 */
package at.fhj.ase.util;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;

import org.apache.commons.codec.digest.DigestUtils;


/**
 * ASE10 - SS2011 
 * Project work Client Architecture and Design
 *  
 * @author Henning Diepold
 * @author Dieter Steiner
 */

public class LoginManager implements Login {

	/**
	 * Open a connection to database. Check, if username exists. - If username
	 * doesn' t exist: access denied. - If username exists: get hashvalue for
	 * given username. - Compare hashvalue from database with calculated
	 * hasvalue from password entered by customer. - If identical: access ok. -
	 * else: access denied.
	 * 
	 */
	private final String JDBC_CONNECT_STRING = "jdbc:mysql://localhost:3306/tinybank";
	private final String HASH_ALGORITHM = "SHA-1";
	private final String USERNAME = "test";
	private final String PASSWORD = "test";

	/**
	 * 
	 */
	public LoginManager() {
	}
	
	public Connection login(final String username, final String password) throws ClassNotFoundException, UnsupportedEncodingException, SQLException {
		if (null == username) {
			throw new NullPointerException("username: null!");
		}
		if ("" == username) {
			throw new IllegalArgumentException("username: empty!");
		}
		if (null == password) {
			throw new NullPointerException("password: null!");
		}
		if ("" == password) {
			throw new IllegalArgumentException("password: empty!");
		}

		Class.forName("com.mysql.jdbc.Driver");
		
		System.out.println("> LoginManager connect string: " + JDBC_CONNECT_STRING + ", " + username + ", " +  mySQLPassword(password));
		
//		Connection connection = DriverManager.getConnection(JDBC_CONNECT_STRING, username, mySQLPassword(password));
		Connection connection = DriverManager.getConnection(JDBC_CONNECT_STRING, username, password);

		return connection;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see at.fhj.ase.util.Login#login(java.lang.String, java.lang.String)
	 */
	public boolean loginDefaultUser(final String username, final String password)
			throws ClassNotFoundException, NoSuchAlgorithmException,
			UnsupportedEncodingException, SQLException, IOException {

		final String uname = username.trim();
		final String pw = password.trim();
		// Is it necessary to check for missing parameters here too, although
		// the values are checked in the proxy?

		if (null == uname) {
			throw new NullPointerException("username: null!");
		}
		if ("" == uname) {
			throw new IllegalArgumentException("username: empty!");
		}
		if (null == pw) {
			throw new NullPointerException("password: null!");
		}
		if ("" == pw) {
			throw new IllegalArgumentException("password: empty!");
		}

		Class.forName("com.mysql.jdbc.Driver");

//		System.out.println(JDBC_CONNECT_STRING + ", " + USERNAME + ", " +  PASSWORD);

		// default login
		Connection connection = DriverManager.getConnection(JDBC_CONNECT_STRING, USERNAME, PASSWORD);

		String selectUser = "select username, password from tinybank.customers where username = ? and password = ?";
		PreparedStatement ps = connection.prepareStatement(selectUser);

		ps.setString(1, uname);
		ps.setString(2, mySQLPassword(pw));
		System.out.println("> LoginManager ps(1): " + uname);
		System.out.println("> LoginManager ps(2): " + mySQLPassword(pw));

		/**
		 * Aim of the following code: if username and password are found in the
		 * table CUSTOMERS than login is ok and the database connection is
		 * returned for further usage otherwise the login is refused.
		 */

		 /*!!! don't do something like that:
		 * ResultSet rs = ps.executeQuery(selectUser);
		 * that causes an SQL syntax error because the sql statement defined
		 * before is overwritten with a string !!!
		 */
		 // todo: error handling if result set is empty
 		 ResultSet rs = ps.executeQuery();
 		 		
		 while(rs.next()) {
			System.out.println("> LoginManager rs(1): " + rs.getString(1));
			System.out.println("> LoginManager rs(2): " + rs.getString(2));

		}
 		 
 		// at the beginning rs pointer is always before the first record
 		 
 		 if(rs.first()) {	// if there is at least one row in the result set: true
 	 		 if (rs.getString(1).equals(uname) && rs.getString(2).equals(mySQLPassword(pw))) {
 				System.out.println("> LoginManager ok: " + rs.getString(1) + " " + rs.getString(2));
 				System.out.println("> LoginManager: Login ok!");
 				rs.close();
 				return true;
 			} else {
 				System.out.println(" LoginManager: rs.getString(1): " + rs.getString(1) + ", LoginManager: username: " + uname);
 				System.out.println(" LoginManager: rs.getString(2): " + rs.getString(2) + ", LoginManager: password: " + mySQLPassword(pw));
 				rs.close();
 				return false;
 			}
 		 }
 		 else {
 			System.err.println("> LoginManager: No customer data. Login denied!");
 			rs.close();
 			return false;
 		 }
		
	}

	/**
	 * @param plainText
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public final String mySQLPassword(final String plainText) throws UnsupportedEncodingException {
		final String pText = plainText.trim();
		if (null == pText) {
			throw new NullPointerException("plainText: null!");
		}
		if ("" == pText) {
			throw new IllegalArgumentException("plainText: empty!");
		}
		
		byte[] utf8 = pText.getBytes("UTF-8");
		byte[] test = DigestUtils.sha(DigestUtils.sha(utf8));
		StringBuilder hex = new StringBuilder();
		for (byte b : test) {
			int i = (b & 0xff);
			hex.append(String.format("%02x", i));
		}
		String hashValue = hex.toString();
		
		return ("*" + hashValue).toUpperCase();
	}

	
	/**
	 * @param algorithm
	 * @param plainText
	 * @return
	 * @throws IOException
	 * @throws NoSuchAlgorithmException
	 * @throws UnsupportedEncodingException
	 */
	public final String calculateHashValue(final String plainText)throws IOException, NoSuchAlgorithmException, UnsupportedEncodingException {
		final String pText = plainText.trim();

		// Is it necessary to check for missing parameters here too, although
		// the values are checked in the proxy?
		if (null == pText) {
			throw new NullPointerException("plainText: null!");
		}
		if ("" == pText) {
			throw new IllegalArgumentException("plainText: empty!");
		}

		MessageDigest md;
		md = MessageDigest.getInstance(HASH_ALGORITHM);
		md.update(pText.getBytes("UTF-8"));
		byte[] bytes = md.digest();
		// String hashValue = new String(bytes);
		StringBuilder hex = new StringBuilder();
		for (byte b : bytes) {
			int i = (b & 0xff);
			hex.append(String.format("%02x", i));
		}
		String hashValue = hex.toString();
//		 String hashValue = Base64Coder.encode(bytes).toString();
//		System.out.println("> password: " + hashValue);
		return "*" + hashValue.toUpperCase();
	}

}
